package org.bluedream.comm.utils.excel;

import lombok.Getter;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.bluedream.comm.utils.EmptyUtil;
import org.bluedream.comm.utils.ReflexUtil;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @ClassName ExcelExport
 * @Description TODO
 * @Author foxsand
 * @Data 2021-08-30 8:37
 * @Version
 */
public class ExcelExport extends BaseExcel implements Serializable {
    private static final long serialVersionUID = 5597585505462001671L;

    //表头：非必要
    @Getter
    private String title;
    //标题行，KEY：实体类field名称，VALUE：excel标题字段名称
    @Getter
    private Map<String , String> header = new LinkedHashMap<>();
    //列数量，用于表头合并单元格
    @Getter
    private int columnCount;

    public void setDataList(List<?> dataList){
        if (dataList.size() > (XLSX_MAX_ROW_NUMBER - 2)){
            throw new ExcelException("ExcelException：需要导出的记录数超出Excel的最大值！最大值：" + (XLSX_MAX_ROW_NUMBER - 2));
        }else if (EmptyUtil.isEmpty(dataList)){
            throw new ExcelException("ExcelException：没有需要导出的数据！");
        }
        this.dataList = dataList;
    }

    /**
     * 创建工作簿
     */
    public void setWorkbook(){
        this.workbook = new XSSFWorkbook();
    }

    /**
     * 创建标题行，按照列数合并单元格
     * @param rowNum 指定标题行的行号
     * @return
     */
    private void createTitle(int rowNum){
        XSSFRow titleRow = (XSSFRow) this.sheet.createRow(rowNum);
        XSSFCell titleCell = addCell(titleRow , 0 , this.title);
        if (this.columnCount > 1){
            //合并列 生成标题行
            CellRangeAddress cellRangeAddress = new CellRangeAddress(rowNum , rowNum , 1 , this.columnCount);
            this.sheet.addMergedRegion(cellRangeAddress);
        }
    }

    /**
     * 创建表头
     * @param rowNum 指定表头行的行号
     * @return
     */
    private void createHeader(int rowNum){
        XSSFRow headerRow = (XSSFRow) this.sheet.createRow(rowNum);
        int cellIndex = 0;
        for (Map.Entry<String , String> map:this.header.entrySet()
             ) {
            Cell headerCell = addCell(headerRow , cellIndex , map.getValue());
            cellIndex++;
        }
    }

    private XSSFRow createDataRow(Sheet sheet , int rowNum , Object entity){
        XSSFRow row = (XSSFRow) sheet.createRow(rowNum);
        Map<String , Object> fieldMap = new HashMap<>();
        List<Field> fieldList = ReflexUtil.getObjectFields(entity);
        int cellIndex = 0;
        for (Map.Entry<String , String> entry:this.header.entrySet()
             ) {
            String fieldName = entry.getKey();
            for (int i = 0; i < fieldList.size(); i++) {
                String fn1 = fieldList.get(i).getName();
                if (fn1.equals(fieldName)){
                    addCell(row , cellIndex , ReflexUtil.getFieldValue(entity , fn1));
                    break;
                }
            }
            cellIndex++;
        }
        return row;
    }

    public ExcelExport(HttpServletResponse response,List<?> dataList , Class<?> clazz , String... args){
        try {
            setDataList(dataList);
            setAnnotationAnalyses(clazz);
            if (this.annotationAnalyses.size() == 0){
                throw new ExcelException("Excel Exception:annotationAnalyses is null");
            }
            setFileAttribute(args);
            response.reset();
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + this.fileName + ".xlsx");
            createWorkbook();
            OutputStream os = response.getOutputStream();
            this.workbook.write(os);
            os.flush();
            os.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void setFileAttribute(String... args){
        String fileName = ExcelExport.DEFAULT_FILE_NAME;
        String sheetName = ExcelExport.DEFAULT_SHEET_NAME;
        if (args.length > 3){
            throw new ExcelException("ExcelException：传入的参数过多！");
        }
        if (args.length == 1 && EmptyUtil.isNoEmpty(args[0])){
            fileName = args[0];
        }
        if (args.length >= 2){
            fileName = EmptyUtil.isNoEmpty(args[0])?args[0]:fileName;
            sheetName = EmptyUtil.isNoEmpty(args[1])?args[1]:sheetName;
        }
        if (args.length == 3 && EmptyUtil.isNoEmpty(args[2])){
            setTitle(args[2]);
        }

        setFileName(fileName);
        setSheetName(sheetName);
    }

    private void createWorkbook(){
        setHeader();
        setWorkbook();
        setSheet();
        int rowIndex = 0;
        // 创建标题行
        if (EmptyUtil.isNoEmpty(this.title)){
            createTitle(rowIndex);
            rowIndex++;
        }
        // 创建表头行
        createHeader(rowIndex);
        //插入数据明细
        rowIndex++;
        for (int i = 0; i < this.dataList.size(); i++) {
            createDataRow(this.sheet , rowIndex , this.dataList.get(i));
            rowIndex++;
        }
    }

    public void setSheet(){
        this.sheet = this.workbook.createSheet(sheetName);
    }

    /**
     * 获取实体类中的注解,如果需要生成导入模板，可以重写该方法。
     * @param clazz
     */
    public void setAnnotationAnalyses(Class<?> clazz){
        this.annotationAnalyses = ExcelUtil.getAnnotationAnalyses(clazz , ExcelType.EXPORT);
        if (this.annotationAnalyses.size() > ExcelExport.XLSX_MAX_COLUMN_NUMBER){
            throw new ExcelException("ExcelException：需要导出的数据列超出Excel允许的最大范围！最大值：" + ExcelExport.XLSX_MAX_COLUMN_NUMBER);
        }
    }

    public void setTitle(String title){
        this.title = title;
    }

    public void setHeader(){
        for (ExcelAnnotationAnalysis en:this.annotationAnalyses
             ) {
            this.header.put(en.getFieldName() , en.getExcelFieldName());
        }
    }

    public void setColumnCount(){
        this.columnCount = this.annotationAnalyses.size();
    }

    private XSSFCell addCell(XSSFRow row , int column , Object val){
        XSSFCell cell = row.createCell(column);
        if (EmptyUtil.isEmpty(val)){
            cell.setCellValue("");
        }else if (val instanceof Integer){
            cell.setCellValue((double)(Integer)val);
        }else if (val instanceof Long){
            cell.setCellValue((double)(Long)val);
        }else if (val instanceof Double){
            cell.setCellValue((Double)val);
        }else if (val instanceof Float){
            cell.setCellValue((double)(Float)val);
        }else if (val instanceof Date){
            cell.setCellValue((Date)val);
        }else if (val instanceof String){
            cell.setCellValue(String.valueOf(val));
        }
        else {
            cell.setCellValue(String.valueOf(val));
        }
        return cell;
    }
}
